/**
 * \file AResolutionLogique.c
 * \author Vincent JACQUEMIN
 * \date 07 Novembre 2013
 *
 * Fichier source AResolutionLogique.c pour le programme du Sudoku
 */


#include "AResolutionLogique.h"

/**
 * \brief Validation d'une case.
 * \param grille : Grille sur laquelle on travaille.
 * \param indiceCase : Indice de la case à valider.
 * \param valeur : La valeur que doit prendre la case.
 *
 * Cette procédure valide une case vide ne possédant qu'un candidat, lui assignant une valeur et faisant en sorte que les cases vides situées dans ses unités excluent cette valeur de leurs candidats.
 */
static void RL_validerCase(SGrille* grille, unsigned indiceCase, unsigned short valeur)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;
    unsigned nombreCases = nombreUnites*nombreUnites;

    /* Vérification des paramètres */
	assert(grille != NULL);
	assert(indiceCase < nombreCases);
	assert(valeur < nombreUnites);

	/* Si la case n'est pas vide, on sort de la procédure */
	if (SC_getEtat(SG_getCase(grille, indiceCase)) != vide)
        return;

	/* On valide la case */
	SC_setValeur(SG_getCase(grille, indiceCase), valeur);
	SC_setEtat(SG_getCase(grille, indiceCase), valide);
	SC_setCandidats(SG_getCase(grille, indiceCase), pow2(valeur));

    /* On parcourt la ligne pour retirer les candidats */
    unsigned short indiceLigne = SC_getIndice(SG_getCase(grille, indiceCase))/nombreUnites;
    for (unsigned i=0; i<nombreUnites; i++)
    {
        if (SC_getEtat(SU_getCase(SG_getLigne(grille, indiceLigne), i)) != vide)
            SC_retirerCandidat(SG_getCase(grille, indiceCase), valeur);
    }

    /* On parcourt la colonne pour retirer les candidats */
    unsigned short indiceColonne = SC_getIndice(SG_getCase(grille, indiceCase))%nombreUnites;
    for (unsigned i=0; i<nombreUnites; i++)
    {
        if (SC_getEtat(SU_getCase(SG_getColonne(grille, indiceColonne), i)) != vide)
            SC_retirerCandidat(SG_getCase(grille, indiceCase), valeur);
    }

    /* On parcourt la région pour retirer les candidats */
    unsigned short indiceRegion = type*(indiceLigne/type) + indiceColonne/type;
    for (unsigned i=0; i<nombreUnites; i++)
    {
        if (SC_getEtat(SU_getCase(SG_getRegion(grille, indiceRegion), i)) != vide)
            SC_retirerCandidat(SG_getCase(grille, indiceCase), valeur);
    }
}


/**
 * \brief Elimination des candidats de toute la grille.
 * \param grille : Grille sur laquelle on travaille (donnée-résultat).
 *
 * Cette procédure élimine les candidats de chaque case vide d'une grille.
 */
void RL_miseAJourTousCandidats(SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
	unsigned short nombreUnites = type*type;
    unsigned nombreCases = nombreUnites*nombreUnites;

    /* Vérification des paramètres */
	assert(grille != NULL);

    /* On va mettre à jour tous les candidats de toutes les cases vides */
    for (unsigned i=0; i<nombreCases; i++)
    {
        if (SC_getEtat(SG_getCase(grille, i)) == vide)
        {
            SC_reinitialiserCandidats(SG_getCase(grille, i));

            /* On parcourt la ligne pour retirer les candidats */
            unsigned short indiceLigne = SC_getIndice(SG_getCase(grille, i))/nombreUnites;
            for (unsigned j=0; j<nombreUnites; j++)
            {
                if (SC_getEtat(SU_getCase(SG_getLigne(grille, indiceLigne), j)) != vide)
                    SC_retirerCandidat(SG_getCase(grille, i), SC_getValeur(SU_getCase(SG_getLigne(grille, indiceLigne), j)));
            }

            /* On parcourt la colonne pour retirer les candidats */
            unsigned short indiceColonne = SC_getIndice(SG_getCase(grille, i))%nombreUnites;
            for (unsigned j=0; j<nombreUnites; j++)
            {
                if (SC_getEtat(SU_getCase(SG_getColonne(grille, indiceColonne), j)) != vide)
                    SC_retirerCandidat(SG_getCase(grille, i), SC_getValeur(SU_getCase(SG_getColonne(grille, indiceColonne), j)));
            }

            /* On récupère l'indice de la région d'une case pour retirer les candidats */
            unsigned short indiceRegion = type*(indiceLigne/type) + indiceColonne/type;
            for (unsigned j=0; j<nombreUnites; j++)
            {
                if (SC_getEtat(SU_getCase(SG_getRegion(grille, indiceRegion), j)) != vide)
                    SC_retirerCandidat(SG_getCase(grille, i), SC_getValeur(SU_getCase(SG_getRegion(grille, indiceRegion), j)));
            }
        }
    }
}


/**
 * \brief Recherche de singletons nus et validation des cases correspondantes.
 * \param grille : Grille dans laquelle l'unité se trouvé (donnée-résultat).
 * \param unite : Unité dans laquelle on effectue l'opération (donnée-résultat).
 * \return Renvoie vrai si une case au moins a été validée, faux sinon.
 *
 * Cette fonction va passer en revue les cases d'une unité pour vérifier, dans l'ordre :
 *    - Si elle est vide (sinon on passe à la case suivante)
 *    - Si elle n'a qu'un seul candidat.
 * Si tel est le cas, la case est validée avec le candidat restant.
 */
static bool RL_singletonsNusUnite(SGrille* grille, SUnite* unite)
{
    /* Variables des convenances */
    unsigned char type = SU_getType(unite);
	unsigned short nombreUnite = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);
	assert(unite != NULL);

	/* On va parcourir chaque case de l'unité */
	bool caseValidee = false;
	for (unsigned short i=0; i<nombreUnite; i++)
	{
	    /* Si la case sur laquelle on travaille est vide et ne possède qu'un candidat */
		if (SC_getEtat(SU_getCase(unite, i)) == vide && SC_getNombreCandidats(SU_getCase(unite, i)) == 1)
		{
		    /* On transforme le candidat en valeur (le log permet de transformer le flag en candidat) */
			RL_validerCase(grille, SC_getIndice(SU_getCase(unite, i)), logBase2(SC_getTousCandidats(SU_getCase(unite, i))));
			caseValidee = true;
		}
	}

	return caseValidee;
}


void RL_resoudreGrille(SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
	unsigned short nombreUnite = type*type;

	/* Vérifications des paramètres */
	assert(grille != NULL);

    /* On met à jour les candidats des cases vides */
	RL_miseAJourTousCandidats(grille);

	/* On va évincer les groupes nus sur les lignes, les colonnes et les régions tant qu'on peut le faire */
	bool recommencer = true;
	while (recommencer)
    {
		/* On boucle sur les lignes */
		for (unsigned short i=0; i<nombreUnite; i++)
		{
			/* On recherche les singletons nus dans la ligne */
			if (!RL_singletonsNusUnite(grille, SG_getLigne(grille, i)))
                recommencer = false;
		}

		/* On boucle sur les colonnes */
		for (unsigned short i=0; i<nombreUnite; i++)
        {
			/* On recherche les singletons nus dans la colonne */
			if (!RL_singletonsNusUnite(grille, SG_getColonne(grille, i)))
                recommencer = false;
		}

		/* On boucle sur les régions */
		for (unsigned short i=0; i<nombreUnite; i++)
		{
			/* On recherche les singletons nus dans la région */
			if (!RL_singletonsNusUnite(grille, SG_getRegion(grille, i)))
                recommencer = false;
		}
	}
}
